﻿#pragma once

#include <unordered_map>
#include <functional>

#include "CommonTime.hpp"
#include "JulianTime.hpp"
#include "GPSTime.hpp"
#include "UnixTime.hpp"

namespace ns_Time
{
    class TimeFactory
    {
    public:
        using TimePtr = std::shared_ptr<TimeSys>;

    public:
        enum class ValueType
        {
            CommonTimeSys,
            GPSTimeSys,
            UnixTimeSys,
            JulianTimeSys
        };

    public:
        template <class... Args>
        static TimePtr MakeTimePtr(ValueType type, Args &&...args)
        {
            switch (type)
            {
            case ValueType::CommonTimeSys:
                if constexpr ((sizeof...(args) == 0 || sizeof...(args) == 1 || sizeof...(args) == 6) && std::is_constructible<CommonTime, Args...>::value)
                    return std::make_shared<CommonTime>(std::forward<Args>(args)...);
                break;
            case ValueType::GPSTimeSys:
                if constexpr ((sizeof...(args) == 0 || sizeof...(args) == 1 || sizeof...(args) == 2) && std::is_constructible<GPSTime, Args...>::value)
                    return std::make_shared<GPSTime>(std::forward<Args>(args)...);
                break;
            case ValueType::JulianTimeSys:
                if constexpr ((sizeof...(args) == 0 || sizeof...(args) == 1 || sizeof...(args) == 2) && std::is_constructible<JulianTime, Args...>::value)
                    return std::make_shared<JulianTime>(std::forward<Args>(args)...);
                break;
            case ValueType::UnixTimeSys:
                if constexpr ((sizeof...(args) == 0 || sizeof...(args) == 1 || sizeof...(args) == 2) && std::is_constructible<UnixTime, Args...>::value)
                    return std::make_shared<UnixTime>(std::forward<Args>(args)...);
                break;
            }

            return nullptr;
        }

        static bool SwitchTo(ValueType type, const TimeFactory::TimePtr &source, TimePtr *out)
        {
            TimePtr ans;

            switch (type)
            {
            case ValueType::CommonTimeSys:
                ans = std::make_shared<CommonTime>();
                break;
            case ValueType::GPSTimeSys:
                ans = std::make_shared<GPSTime>();
                break;
            case ValueType::JulianTimeSys:
                ans = std::make_shared<JulianTime>();
                break;
            case ValueType::UnixTimeSys:
                ans = std::make_shared<UnixTime>();
                break;
            default:
                *out = nullptr;
                return false;
            }

            TimeSys::SecondCount count = source->SecondFromRefTime();
            ans.reset(ans->GetRefTime()->TimeAfterSecond(count));

            *out = ans;

            return true;
        }
    };
}